import React, { useEffect, useState } from "react";
import { toast } from "../../../../components/Toast/Toast";
import styles from "./Inject.module.css";
import TextArea from "antd/es/input/TextArea";
import { Button, Form, Modal, Space, Input } from "antd";
import {
  addSubscribe,
  BROKER_URL,
  setShowModal,
  updateConnection,
  WebSocketMsg,
} from "../../../../utils/store/socketSlice";
import { useAppDispatch, useAppSelector } from "../../../../utils/store/hooks";
import { copyText } from "../../../../utils/common/copy";
import { BASE } from "../../../../utils/utils/request";
import {
  injectApi,
  randomApi,
  uploadApi,
} from "../../../../utils/backend/server";
import { useHotKey } from "../../../../utils/utils/shortcut";
import { client, Client } from "stompjs";

const SCRIPT_KEY = "SCRIPT_INJECT";

const Script = () => {
  const [form] = Form.useForm();
  // const [content, setContent] = useState("");
  const showModal = useAppSelector((state) => state.socket).showModal;
  const dispatch = useAppDispatch();
  const hotKey = useHotKey("s");

  useEffect(() => {
    hotKey && showModal && apply();
  }, [hotKey]);

  useEffect(() => {
    if (!showModal) return;
    randomApi.getRandom({ name: SCRIPT_KEY }).then((res) => {
      if (!res.data) return;
      const url = `upload/${res.data.value}`;
      const scriptRef = `<script src="${BASE}${url}"></script>`;
      form.setFieldValue("reference", scriptRef);
      injectApi
        .fetchScript(url)
        .then((res) => {
          console.log(res, "txt");
          form.setFieldValue("content", res.data);
          // setContent(res.data);
        })
        .catch((err) => {
          console.log(err, "err");
          form.setFieldValue("reference", "file does not exist!");
          form.setFieldValue("content", "file does not exist!");
        });
    });
  }, [showModal]);

  const apply = async () => {
    const content = form.getFieldValue("content");
    if (content === "") return toast("please check your content!");
    const blob = new Blob([content], { type: "application/javascript" });
    const file = new File([blob], "filename.js", { type: blob.type });
    const formData = new FormData();
    formData.append("file", file);
    uploadApi.uploadFiles(formData).then((res) => {
      const fileName = res.data.data[0];
      randomApi
        .addRandom({
          name: SCRIPT_KEY,
          value: fileName,
        })
        .then((res) => {
          console.log(res);
          toast("apply new script success!");
          dispatch(setShowModal(false));
        });
    });
  };

  return (
    <>
      <Form form={form} layout="horizontal" onFinish={apply}>
        <Form.Item
          name="reference"
          label="reference"
          labelCol={{ span: 6 }}
          wrapperCol={{ span: 16 }}
        >
          <Input />
        </Form.Item>
        <Form.Item
          name="content"
          label="content"
          labelCol={{ span: 6 }}
          wrapperCol={{ span: 16 }}
          rules={[{ required: true }]}
        >
          <TextArea
            placeholder="You can change script content here!"
            rows={8}
          />
        </Form.Item>
        <Form.Item wrapperCol={{ span: 14, offset: 6 }}>
          <Space wrap>
            <Button htmlType="submit" type="primary">
              apply
            </Button>
            <Button htmlType="button" onClick={() => form.resetFields()}>
              Reset
            </Button>
            <Button
              htmlType="button"
              onClick={() =>
                copyText(form.getFieldValue("reference")).then(() =>
                  toast("copied!")
                )
              }
            >
              Copy
            </Button>
          </Space>
        </Form.Item>
      </Form>
    </>
  );
};

export const Inject = () => {
  const [code, setCode] = useState<string>("");
  const [output, setOutput] = useState<string>("");
  const { showModal, connected } = useAppSelector((state) => state.socket);
  const [user, setUser] = useState({
    sender: "master",
    receiver: "slave",
  });
  const [stompClient, setStompClient] = useState<Client | null>(null);
  const [timer, setTimer] = useState<number>();
  const dispatch = useAppDispatch();

  const connect = () => setStompClient(client(BROKER_URL));

  useEffect(() => {
    try {
      const identity = JSON.parse(
        (localStorage.getItem("socket_identity") as string) || "{}"
      );
      identity.sender && identity.receiver && setUser(identity);
    } catch (err) {
      console.log(err);
    }
  }, []);

  useEffect(() => {
    if (stompClient === null || stompClient.ws.readyState !== 0) return;

    stompClient.connect(
      {},
      () => {
        dispatch(updateConnection(true));
        toast("connected!");
        subscribe();
      },
      () => {
        toast("disconnect");
        dispatch(updateConnection(false));
      }
    );
    setTimer(
      setInterval(() => {
        console.log(stompClient.ws.readyState, "stompClient");
        if (stompClient === null || stompClient.ws.readyState !== 1) return;
        stompClient.send(
          "/app/send",
          {},
          JSON.stringify({
            msg: "heartbeat",
            sender: user.sender,
            receiver: user.receiver,
          })
        );
      }, 5000) as unknown as number
    );

    return () => {
      clearInterval(timer);
      disconnect();
    };
  }, [stompClient]);

  const disconnect = () => {
    connected &&
      stompClient &&
      stompClient.disconnect(() => {
        dispatch(updateConnection(false));
      });
  };

  const subscribe = () => {
    const id = "/user/" + user.sender;
    toast("subscribe: " + user.sender);
    stompClient?.subscribe(id, (msg) => {
      let body: Partial<WebSocketMsg> = {};
      try {
        body = JSON.parse(msg.body);
        if (body.msg === "heartbeat") return;
      } catch (err) {
        console.log(err);
      }
      setOutput(
        (e) => (e.endsWith("\n") || e.length === 0 ? e : e + "\n") + body.msg
      );
    });
  };

  const send = () => {
    if (code.length <= 0) return toast("please check your code!");
    if (stompClient === null) return toast("please connect first!");

    stompClient.send(
      "/app/send",
      {},
      JSON.stringify({
        msg: code,
        sender: user.sender,
        receiver: user.receiver,
      })
    );
    setCode("");
  };

  return (
    <div className={styles.box}>
      <TextArea
        onChange={(e) => setOutput(e.target.value)}
        value={output}
        style={{ height: 180 }}
        readOnly
      />
      <TextArea
        style={{ height: 220 }}
        onChange={(e) => setCode(e.target.value)}
        showCount
        value={code}
      />
      <div className={styles.circle}>
        <Space>
          <Button onClick={send} className={styles.but} type="primary">
            send
          </Button>
          <Button onClick={disconnect} className={styles.but} type="default">
            disconnect
          </Button>
          <Button
            onClick={() => dispatch(setShowModal(true))}
            className={styles.but}
            type="default"
          >
            script
          </Button>
          <Button onClick={() => setOutput("")}>clear</Button>
        </Space>
      </div>
      <div className={styles.circle}>
        <Space>
          <Input
            value={user.sender}
            placeholder="sender"
            onChange={(e) =>
              setUser((user) => ({ ...user, sender: e.target.value }))
            }
          />
          <Button onClick={connect} className={styles.but} type="primary">
            connect
          </Button>
        </Space>
      </div>
      <div className={styles.circle}>
        <Space>
          <Input
            value={user.receiver}
            placeholder="receiver"
            onChange={(e) =>
              setUser((user) => ({ ...user, receiver: e.target.value }))
            }
          />
          <Button onClick={subscribe} className={styles.but} type="primary">
            subscribe
          </Button>
        </Space>
      </div>

      <Modal
        open={showModal}
        footer={null}
        onCancel={() => dispatch(setShowModal(false))}
      >
        <Script />
      </Modal>
    </div>
  );
};
